# Problem Set 4
# Name: Noah Spahn    
# Collaborators: 
# Time: 5:00 hrs

#
# Problem 1
#
def near(inputInt,idealInt,epsilon=.01):
    '''
    Returns a boolean flag to indicate weather or not the inputInt value is within epsilon precision of idealInt
    inputInt : int
    idealInt : int
    epsilon : float (optional parameter)
    '''
    #print "\t:: method: near()"
    if inputInt - idealInt <= epsilon: 
        flag = 1
        #print inputInt, "is near", idealInt
    else: 
        flag = 0
        #print "\t", inputInt, "is NOT near", idealInt
    return flag    
    
    
def nestEggFixed(salary, save, growthRate, years):
    """
    - salary: the amount of money you make each year.
    - save: the percent of your salary to save in the investment account each
      year (an integer between 0 and 100).
    - growthRate: the annual percent increase in your investment account (an
      integer between 0 and 100).
    - years: the number of years to work.
    - return: a list whose values are the size of your retirement account at
      the end of each year.
    """
    fund = []

    for i in range(years):
        if i==0:fund.append(salary*save*0.01)
        else: fund.append(fund[i-1]*(1+0.01*growthRate)+salary*save*0.01)
        
    return fund

def testNestEggFixed():
    salary =10000
    save =10
    growthRate=15
    years=5
    
    savingsRecord = nestEggFixed(salary, save, growthRate, years)
    print savingsRecord
    assert savingsRecord[0] == 1000.0
    # Output should have values close to:
    testValues = [1000.0, 2150.0, 3472.5, 4993.375, 6742.3812499999995]
    for i in range(len(savingsRecord)):
        assert near(savingsRecord[i], testValues[i]), 'Assertion failed'
        i+=i   
#
# Problem 2
#
def nestEggVariable(salary, save, growthRates):
    """
    - salary: the amount of money you make each year.
    - save: the percent of your salary to save in the investment account each
      year (an integer between 0 and 100).
    - growthRate: a list of the annual percent increases in your investment
      account (integers between 0 and 100).
    - return: a list of your retirement account value at the end of each year.
    """
    assert (save > 0), "Savings must contain positive value"
    fund = []
    for i in range(len(growthRates)):
        if i==0:fund.append(salary*save*0.01)
        else: fund.append(fund[i-1]*(1+0.01*growthRates[i])+salary*save*0.01)
    return fund
   
def testNestEggVariable():
    salary      = 10000
    save        = 10
    growthRates = [3, 4, 5, 0, 3]
    savingsRecord = nestEggVariable(salary, save, growthRates)
    print savingsRecord
    # Output should have values close to:
    testValues = [1000.0, 2040.0, 3142.0, 4142.0, 5266.2600000000002]

    for i in range(len(savingsRecord)):
        assert near(savingsRecord[i], testValues[i]), 'Assertion failed'
        i+=i
#
# Problem 3
#

def postRetirement(savings, growthRates, expenses):
    """
    - savings: the initial amount of money in your savings account.
    - growthRate: a list of the annual percent increases in your investment
      account (an integer between 0 and 100).
    - expenses: the amount of money you plan to spend each year during
      retirement.
    - return: a list of your retirement account value at the end of each year.
    """
    fund = []
    for i in range(len(growthRates)):
        if i==0:fund.append(savings*(1+0.01*growthRates[i])-expenses)
        else: fund.append(fund[i-1]*(1+0.01*growthRates[i])-expenses)
    return fund

def testPostRetirement():
    savings     = 100000
    growthRates = [10, 5, 0, 5, 1]
    expenses    = 30000
    savingsRecord = postRetirement(savings, growthRates, expenses)
    print savingsRecord
    # Output should have values close to:
    testValues = [80000.000000000015, 54000.000000000015, 24000.000000000015,-4799.9999999999854, -34847.999999999985]
    for i in range(len(savingsRecord)):
        assert near(savingsRecord[i],testValues[i])
        
    savings     = 50000
    growthRates = [0, 0, 0, 0, 0]
    expenses    = 10000
    savingsRecord = postRetirement(savings, growthRates, expenses)
    print savingsRecord
    # Output should have values near to:
    testValues = [40000, 30000, 20000,10000, 0]
    for i in range(len(savingsRecord)):
        assert near(savingsRecord[i],testValues[i])
    
#
# Problem 4
#

def findMaxExpenses(salary, save, preRetireGrowthRates, postRetireGrowthRates,
                    epsilon):
    """
    - salary: the amount of money you make each year.
    - save: the percent of your salary to save in the investment account each
      year (an integer between 0 and 100).
    - preRetireGrowthRates: a list of annual growth percentages on investments
      while you are still working.
    - postRetireGrowthRates: a list of annual growth percentages on investments
      while you are retired.
    - epsilon: an upper bound on the absolute value of the amount remaining in
      the investment fund at the end of retirement.
    """
    epsilon = 0.5
    expenses = 1000
    savingsRecord = nestEggVariable(salary, save, preRetireGrowthRates)
    savings = savingsRecord[-1]
    
    sRecord = postRetirement(savings, postRetireGrowthRates, expenses)

    death = sRecord[-1]    
    low  = 0
    high = savings
    ctr = 1
    while not near(abs(death), epsilon) and ctr < 20:
        #print "death:",death," epsilon:",epsilon     
        if death > epsilon: # too high
            #print "L",
            low = expenses
        else: 
            #print "H",
            high = expenses # it was too low
        expenses = (low+high)/2.0
        
        b = postRetirement(savings, postRetireGrowthRates, expenses)
        death = b[-1]
        ctr += 1
    #print 'expens method. Num iterations:',ctr, 'Estimate:', expenses
    return expenses

def testFindMaxExpenses():
    salary                = 10000
    save                  = 10
    preRetireGrowthRates  = [3, 4, 5, 0, 3]
    postRetireGrowthRates = [10, 5, 0, 5, 1]
    epsilon               = 0.01
    expenses = findMaxExpenses(salary, save, preRetireGrowthRates, postRetireGrowthRates, epsilon)
    print expenses
    # Output should have a value close to:
    assert near(expenses,1229.95548986,epsilon)
    
def tests():
    print "::testNestEggFixed\t",
    testNestEggFixed()
    print "::testNestEggVariable\t",
    testNestEggVariable()
    print "::testPostRetirement\t",
    testPostRetirement()
    print "::testFindMaxExpenses\t",
    testFindMaxExpenses()
      
#tests()